package cn.yeziji.forum.service.websocket;

import cn.yeziji.forum.common.websocket.Chatroom;
import cn.yeziji.forum.factory.websocket.WebsocketOperationExecutioner;
import cn.yeziji.forum.utils.SpringUtils;
import com.alibaba.fastjson.JSONObject;
import com.github.benmanes.caffeine.cache.Cache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Websocket 服务类
 *
 * @author gzkemays
 * @since 2022/3/24 8:59
 * @deprecated 改用 netty 服务单独处理
 */
@Slf4j
@Component(value = "websocketService")
@ServerEndpoint("/ws/{name}")
public class WebsocketService {
  /** 与某个客户端的连接对话，需要通过它来给客户端发送消息 */
  private Session session;

  /** 标识当前连接客户端的用户名 */
  private String name;

  /** 用于存所有的连接服务的客户端，这个对象存储是安全的 */
  private static final ConcurrentHashMap<String, WebsocketService> WEB_SOCKET_SET =
      new ConcurrentHashMap<>();

  private Cache<String, String> getCache() {
    return SpringUtils.getBean("userWebsocketCache");
  }

  @OnOpen
  public void open(Session session, @PathParam(value = "name") String name) {
    this.session = session;
    this.name = name;
    // 如果已重复则重新刷新加载一遍
    if (WEB_SOCKET_SET.containsKey(name)) {
      WEB_SOCKET_SET.remove(name);
      WEB_SOCKET_SET.put(name, this);
    } else {
      WEB_SOCKET_SET.put(name, this);
      log.info("用户{}进入", name);
    }
    // name是用来表示唯一客户端，如果需要指定发送，需要指定发送通过name来区分
    WEB_SOCKET_SET.put(name, this);
    getCache().put(name, "login");
    log.info("[WebSocket] 连接成功，当前连接人数为：={}", WEB_SOCKET_SET.size());
  }

  @OnClose
  public void close() {
    WEB_SOCKET_SET.remove(this.name);
    getCache().invalidate(name);
    log.info("[WebSocket] {} 退出成功，当前连接人数为：={}", this.name, WEB_SOCKET_SET.size());
  }

  @OnMessage
  public void message(String message) {
    log.info("[WebSocket] 收到消息：{}", message);
    JSONObject json = JSONObject.parseObject(message);
    // 聊天室必须携带 key 字段
    if (Objects.nonNull(json.get(Chatroom.KEY))) {
      WebsocketOperationExecutioner.execute(JSONObject.toJavaObject(json, Chatroom.class));
    }
  }

  /**
   * 群发
   *
   * @param message 群发信息
   */
  public void groupSending(String message) {
    for (String name : WEB_SOCKET_SET.keySet()) {
      try {
        WEB_SOCKET_SET.get(name).session.getBasicRemote().sendText(message);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * 指定发送
   *
   * @param name 指定用户名
   * @param message 发送的信息
   */
  public void appointSending(String name, String message) {
    try {
      WEB_SOCKET_SET.get(name).session.getBasicRemote().sendText(message);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public ConcurrentHashMap<String, WebsocketService> getConcurrentMap() {
    return WEB_SOCKET_SET;
  }
}
